extern "C" {
#include <sys/stat.h>
}
#include "base.hpp"
#include "utils.hpp"
#ifndef __CONNECTION_HPP__
#define __CONNECTION_HPP__

/**
 * HTTP コネクション．
 */
class Connection {
private:
  const int _soc;
  const string_map _env;
  string_map  _req;
  string_map  _res;

  static const char* get_char_ptr(
      const std::map<std::string, std::string>& m, const char* key) {
    std::map<std::string, std::string>::const_iterator cit = m.find(key);
    if (cit == m.end()) {
      return NULL;
    } else {
      return cit->second.c_str();
    }
  }

  static bool equals(const char* l, const char* r) {
    if (l == r) {
      return true;
    }
    return l != NULL && r != NULL && strcmp(l, r) == 0;
  }

public:
  Connection(const int socket, const string_map& env)
    : _soc(socket), _env(env)
  {}
  ~Connection() {
    close(this->_soc);
    this->clear_map();
  }

  void clear_map() {
    this->_req.clear();
    this->_res.clear();
  }

  // get directly
  const int c_socket() const { return this->_soc; }
  const string_map& env() const { return this->_env; }
  string_map& request() { return this->_req; }
  string_map& response() { return this->_res; }

  // get value
  const char* get_env(const char* key) const { return get_char_ptr(this->_env, key); }
  const char* get_req(const char* key) const { return get_char_ptr(this->_req, key); }
  const char* get_res(const char* key) const { return get_char_ptr(this->_res, key); }

  bool req_has(const char* key) const {
    return get_char_ptr(this->_req, key) != NULL;
  }

  bool res_has(const char* key) const {
    return get_char_ptr(this->_res, key) != NULL;
  }

  bool req_contains_close() const {
    return this->req_compare("Connection", "close");
  }

  bool res_contains_close() const {
    return this->res_compare("Connection", "close");
  }

  // compare value
  bool req_compare(const char* key, const char* value) const {
    return equals(this->get_req(key), value);
  }

  bool res_compare(const char* key, const char* value) const {
    return equals(this->get_res(key), value);
  }

  // set value
  int set_req(const char* key, const char* value) {
    if (key == NULL || value == NULL) {
      return -1;
    }
    this->_req.insert(string_map::value_type(key, value));
    return 0;
  }

  // response
  int set_res(const char* key, const std::string& value) {
    if (key == NULL) {
      return -1;
    }
    this->_res.insert(string_map::value_type(key, value));
    return 0;
  }

  int set_res(const char* key, const char* value) {
    if (key == NULL || value == NULL) {
      return -1;
    }
    return this->set_res(key, std::string(value));
  }

  int set_res(const char* key, const off_t value) {
    if (key == NULL) {
      return -1;
    }
    return this->set_res(key, util::to_string(value));
  }

public:
  bool send_status_line(const char* ver, const char* status) const;
  bool send_header() const;

  bool send_http_status(const char* ver, const char* status) const;
  bool send(const char* bytes, const int size) const;

  const struct stat& get_file_stat() const { return this->_fstat; }
  const char* get_file_path() const { return this->_fpath.c_str(); }

  void set_physical_file(const std::string& path, const struct stat& st) {
    this->_fpath = path;
    this->_fstat = st;
  }

private:
  struct stat _fstat;
  std::string _fpath;
};
#endif
